import pandas as pd
import jieba
import numpy as np
from pyecharts.charts import Liquid, Pie, Bar, Page, Tab, Grid, Funnel
from pyecharts import options as opts
from pyecharts.commons.utils import JsCode
from wordcloud import WordCloud


def read_filename(fileName):
    return pd.read_excel(fileName)


# 参数 params 是 formatter 需要的单个数据集
#    // 数据名，类目名
#    name: string,
#    // 传入的数据值
#    value: number|Array,
fn = """
    function(params) {
        if(params.name == 'other')
            return '\\n\\n\\n' + params.name + ' : ' + params.value + '%';
        return params.name + ' : ' + params.value + '%';
    }
    """


# 设置标签
def new_label_opts():
    # formatter ：标签内容格式器-->这里采用'回调函数格式'
    return opts.LabelOpts(formatter=JsCode(fn), position="center", font_size=16)


def bar_figure(sub_rank):
    df_stat = read_filename(sub_rank)
    x_data = ['1~30', '31~60', '61~90', '91~120', '121~150', '151~180',
              '181~210', '211~240', '241~270', '271~300', '301~330', '331~360']
    y_data1 = df_stat['当前被收藏数'][:12].tolist()
    y_data2 = df_stat['总书评数'][:12].tolist()
    y_data3 = df_stat['全文字数'][:12].tolist()
    bar = (
        Bar(init_opts=opts.InitOpts(width='1300px', height='670px'))
            .add_xaxis(x_data)
            .add_yaxis("当前被收藏数", y_data1)
            .add_yaxis("总书评数", y_data2)
            .add_yaxis("全文字数", y_data3)
            .set_global_opts(
            # 标题配置
            title_opts=opts.TitleOpts(
                title="营养液数排行前360作者的被收藏数、总书评数、全文字数对比",
                title_link="A_tab.html", title_target='self',
                pos_left='3%', pos_top='2%', title_textstyle_opts=(opts.TextStyleOpts(color='#4c3333'))
            ),
            # 工具箱配置
            toolbox_opts=opts.ToolboxOpts(
                feature=opts.ToolBoxFeatureOpts(
                    save_as_image=opts.ToolBoxFeatureSaveAsImageOpts(is_show=False),
                    data_zoom=opts.ToolBoxFeatureDataZoomOpts(is_show=False)
                ),
            ),
            # 图例配置
            legend_opts=opts.LegendOpts(
                pos_left='45%', pos_top='4%', item_gap=15
            ),
            # x轴配置
            xaxis_opts=opts.AxisOpts(
                name='排名',
                name_location='center',
                name_gap=40,
                name_textstyle_opts=opts.TextStyleOpts(
                    font_size=20, font_weight='bold'
                ),
                axislabel_opts=opts.LabelOpts(font_size=14, rotate=-15)
            ),
            # y轴配置
            yaxis_opts=opts.AxisOpts(
                axislabel_opts=opts.LabelOpts(font_size=14)
            )
        )
            .set_series_opts(
            # 标签配置
            label_opts=opts.LabelOpts(is_show=False),
            # 标记线配置
            markline_opts=opts.MarkLineOpts(
                data=[
                    opts.MarkLineItem(type_="average", name="平均值"),
                ]
            ),
        )
    )
    return bar


def rose_pie_figure(view):
    df_view = read_filename(view)
    a = df_view['作品视角'].tolist()
    # a : ['主受', '女主', '主攻', '互攻', '悲剧', '男主', '不明']
    b = df_view['数量'].tolist()
    # b : [370, 242, 146, 74, 48, 42, 35]
    data = [list(z) for z in zip(a, b)]
    # data : [['主受', 370], ['女主', 242], ['主攻', 146], ['互攻', 74], ['悲剧', 48], ['男主', 42], ['不明', 35]]
    rose_pie = (
        Pie(init_opts=opts.InitOpts(width='1300px', height='680px'))
            .add(
            "",
            data_pair=data,
            radius=["20%", "70%"],
            center=["50%", "55%"],
            rosetype="area",

        )
            .set_global_opts(
            # 标题
            title_opts=opts.TitleOpts(
                title="作品视角", title_link="A_tab.html", title_target='self',
                pos_left='3%', pos_top='3%',
                title_textstyle_opts=(opts.TextStyleOpts(color='#4c3333'))
            ),
            # 图例
            legend_opts=opts.LegendOpts(
                pos_top="5%", item_gap=15, legend_icon='diamond'
            ),
        )
            .set_series_opts(
            # 标签
            label_opts=opts.LabelOpts(
                font_size=15, font_weight='bold'
            )
        )
    )
    return rose_pie


def pies_figure1(style):
    df_style = read_filename(style)
    total_style = sum(df_style['数量'])
    A = df_style['数量'][0]
    B = df_style['数量'][1]
    C = df_style['数量'][2]
    D = df_style['数量'][3]
    pie = (
        Pie(init_opts=opts.InitOpts(width='1300px', height='680px'))
            .add(
            "",
            # round（a, x）:round函数用来对某数值进行四舍五入
            [['轻松', round(A / total_style, 2) * 10000 / 100],
             ['other', round(1 - A / total_style, 2) * 100]],
            # center : 饼图的中心（圆心）坐标，数组的第一项是横坐标，第二项是纵坐标
            center=["25%", "30%"],
            # radius : 饼图的半径，数组的第一项是内半径，第二项是外半径
            radius=[70, 100],
            # 标签 ：new_label_opts()调用函数
            label_opts=new_label_opts(),
        )
            .add(
            "",
            [['正剧', round(B / total_style, 2) * 100],
             ['other', round(1 - B / total_style, 2) * 100]],
            center=["62%", "30%"],
            radius=[70, 100],
            label_opts=new_label_opts(),
        )
            .add(
            "",
            [['爆笑', round(C / total_style, 2) * 1000 / 10],
             ['other', round(1 - C / total_style, 2) * 100]],
            center=["25%", "72%"],
            radius=[70, 100],
            label_opts=new_label_opts(),
        )
            .add(
            "",
            [['悲剧', round(D / total_style, 2) * 100],
             ['other', round(1 - D / total_style, 2) * 100]],
            center=["62%", "72%"],
            radius=[70, 100],
            label_opts=new_label_opts(),
        )
            .set_global_opts(
            # 标题 :
            # title_link : 主标题跳转 URL 链接
            # title_textstyle_opts : 主标题字体样式配置项
            # title_target : 主标题跳转链接方式
            title_opts=opts.TitleOpts(
                title="作品风格占比", title_link="A_tab.html",
                title_target='self',pos_left='3%', pos_top='3%',
                title_textstyle_opts=(opts.TextStyleOpts(color='#4c3333'))
            ),
            # 图例 :
            # orient：图例列表的布局朝向
            # item_gap：图例每项之间的间隔
            # legend_icon：图例项的 icon（样式）
            legend_opts=opts.LegendOpts(
                pos_top="20%", pos_left="85%", orient="vertical",
                item_gap=20, legend_icon='circle'
            ),
        )
    )
    return pie


def funnel_figure(writer_rank):
    df_rank = read_filename(writer_rank)
    x_data = df_rank['作者'][:12]
    y_data = df_rank['营养液数'][:12].tolist()
    data = [[x_data[i], y_data[i]] for i in range(len(x_data))]
    # data : [['壶鱼辣椒', 4984265], ['红刺北', 3365010], ['燕孤鸿', 3257361],......]
    funnel = (
        Funnel(init_opts=opts.InitOpts(width="1300px", height="680px"))
            .add(
            series_name='作品营养液数',
            data_pair=data,
            # 间距
            gap=2,
            # 提示框配置
            tooltip_opts=opts.TooltipOpts(
                # trigger：触发类型
                # formatter：标签内容格式器-->这里采用‘字符串模板’
                # {a}：系列名    {b}：数据名     {c}：数据值。
                trigger="item", formatter="{a} <br/>{b} : {c}"
            ),
            # 标签
            label_opts=opts.LabelOpts(
                is_show=True, position="inside", font_size=13
            ),
            # 图元样式
            itemstyle_opts=opts.ItemStyleOpts(
                border_color="#fff", border_width=2
            )
        )
            .set_global_opts(
            title_opts=opts.TitleOpts(
                title="营养液数排行前12的作者", title_link="A_tab.html",
                title_target='self', pos_left='3%', pos_top='3%',
                title_textstyle_opts=(opts.TextStyleOpts(color='#4c3333'))
            ),
            legend_opts=opts.LegendOpts(
                pos_top="18%", pos_left="93%", orient="vertical", item_gap=16
            )
        )
    )
    return funnel


def pies_figure2(style4):
    df_style4 = read_filename(style4)
    c = (
        Pie(init_opts=opts.InitOpts(width='1000px', height='680px'))
            .add_dataset(
            source=[
                ["author", "壶鱼辣椒", "红刺北", "燕孤鸿", "桑沃"],
                ['轻松'] + df_style4['轻松'].tolist(),
                ['正剧'] + df_style4['正剧'].tolist(),
                ['爆笑'] + df_style4['爆笑'].tolist(),
                ['悲剧'] + df_style4['悲剧'].tolist(),
            ]
        )
            .add(
            series_name="轻松",
            data_pair=[],
            radius=90,
            center=["35%", "35%"],
            # encode：可以定义 data 的哪个维度被编码成什么。
            encode={"itemName": "author", "value": "壶鱼辣椒"},
        )
            .add(
            series_name="正剧",
            data_pair=[],
            radius=90,
            center=["80%", "35%"],
            encode={"itemName": "author", "value": "红刺北"},
        )
            .add(
            series_name="爆笑",
            data_pair=[],
            radius=90,
            center=["35%", "75%"],
            encode={"itemName": "author", "value": "燕孤鸿"},
        )
            .add(
            series_name="悲剧",
            data_pair=[],
            radius=90,
            center=["80%", "75%"],
            encode={"itemName": "author", "value": "桑沃"},
        )
            .set_global_opts(
            # 标题
            title_opts=opts.TitleOpts(
                title="营养液数排行前4作者的风格占比", title_link="A_tab.html",
                title_target='self', pos_left='3%', pos_top='3%',
                title_textstyle_opts=(opts.TextStyleOpts(color='#4c3333'))
            ),
            # 图例
            legend_opts=opts.LegendOpts(
                pos_left="45%", pos_top="3%", item_gap=15
            ),
        )
            .set_series_opts(
            # 标签
            label_opts=opts.LabelOpts(
                font_size=15, font_weight='bold'
            )
        )
    )
    return c


def liquid_figure(book):
    df_book = read_filename(book)
    total = len(df_book)
    end = round(df_book['文章进度'].value_counts()['完结'] / total, 2)
    publish = round(df_book['出版情况'].value_counts()['已出版'] / total, 2)
    sign = round(df_book['签约状态'].value_counts()['已签约'] / total, 2)
    l1 = (
        # [0.45, 0.5]  第一个数值表示实际数字的百分比，第二个数字表示蓝色的浪的大小
        Liquid()
            .add("完结率", [end, end], center=["25%", "60%"], outline_border_distance=5)
            .set_global_opts(
            title_opts=opts.TitleOpts(
                title="完结率", title_link="A_tab.html", title_target='self',
                pos_left='22%', pos_top='20%',
                title_textstyle_opts=(
                    opts.TextStyleOpts(color='#1668c9', font_size=23)
                )
            )
        )
    )
    l2 = (
        Liquid()
            .add("出版率", [publish, publish], center=["50%", "60%"], outline_border_distance=5)
            .set_global_opts(
            title_opts=opts.TitleOpts(
                title="出版率", title_link="A_tab.html", title_target='self',
                pos_left='47%', pos_top='20%',
                title_textstyle_opts=(
                    opts.TextStyleOpts(color='#1668c9', font_size=23)
                )
            )
        )
    )
    l3 = (
        Liquid()
            .add("签约率", [sign, sign], center=["75%", "60%"], outline_border_distance=5)
            .set_global_opts(
            title_opts=opts.TitleOpts(
                title="签约率", title_link="A_tab.html", title_target='self',
                pos_left='72%', pos_top='20%',
                title_textstyle_opts=(
                    opts.TextStyleOpts(color='#1668c9', font_size=23)
                )
            )
        )
    )

    grid = (
        Grid(init_opts=opts.InitOpts(width='1400px', height='480px'))
            .add(l1, grid_opts=opts.GridOpts())
            .add(l2, grid_opts=opts.GridOpts())
            .add(l3, grid_opts=opts.GridOpts())
    )
    return grid


def word_cloud(comments):
    df_book = read_filename(comments)
    # 评论分词
    comments = ' '.join(jieba.cut_for_search(str([comment for comment in df_book['书评']])))
    # 停用词
    stopwords = [line.strip() for line in open('file/stop_words.txt', encoding='utf-8').readlines()]
    comments_word = ''
    for word in comments:
        if word not in stopwords:
            if word != '\t' and '\n' and ' ':
                comments_word += word
    comments_word = comments_word.replace("   ", " ").replace("  ", " ")
    from PIL import Image
    # 词云模板
    mask = np.array(Image.open("file/book.png"))
    wc = WordCloud(
        # 中文需设置路径
        font_path='C:/Windows/Fonts/SimHei.ttf',
        margin=2,  # 页面边缘
        mask=mask,  # 背景图形，如果想根据图片绘制，则需要设置
        scale=3,  # 规模
        max_words=200,  # 最多词个数
        min_font_size=15,  # 最小字号
        stopwords=stopwords,  # 停止词设置，修正词云图时需要设置
        random_state=42,  # 为每个单词返回一个PIL颜色
        background_color='white',  # 背景颜色
        max_font_size=200,
        colormap='tab20b'
    ).generate(comments_word)
    # 存储图像
    wc.to_file('render/wordcloud.png')
    from pyecharts.components import Image
    image = (
        Image()
            .add(
            src='wordcloud.png',
            style_opts={"width": "900px"},
        )
            .set_global_opts(
            title_opts=opts.ComponentTitleOpts(
                title="词云图", title_style={
                    "style": "color: #4c3333; font-size : 20px; font-weight : bold; "
                             "margin-top : 20px; margin-left : 45px;"}
            )
        )
    )
    return image


def page_layout(comments, style, style4, writer_rank, book, view, sub_rank):
    page = Page(layout=Page.DraggablePageLayout)
    page.add(
        bar_figure(sub_rank),
        rose_pie_figure(view),
        pies_figure1(style),
        funnel_figure(writer_rank),
        pies_figure2(style4),
        liquid_figure(book),
        word_cloud(comments)
    )
    page.render('render/A_page.html')
    page.save_resize_html('render/A_page.html', cfg_file='file/chart_config.json',
                          dest='render/page_resize.html')


def tab_layout(comments, style, style4, writer_rank, book, view, sub_rank):
    tab = (
        Tab('江南小说网数据分析可视化tab')
            .add(bar_figure(sub_rank), "收藏、书评、字数")
            .add(rose_pie_figure(view), "作品视角")
            .add(pies_figure1(style), "整体风格")
            .add(funnel_figure(writer_rank), "营养液")
            .add(pies_figure2(style4), "个体风格")
            .add(liquid_figure(book), "比率")
            .add(word_cloud(comments), "评论")
    )
    tab.render("render/A_tab.html")


if __name__ == '__main__':
    book_fileName = 'file/book.xlsx'  # 总数据
    style_fileName = 'file/book_style.xlsx'  # 风格
    style_head4_fileName = 'file/book_style_head4.xlsx'  # 排行前4的作者作品风格
    writer_rank_fileName = 'file/book_writer_rank.xlsx'  # 作者排行
    comments_fileName = 'file/book_comments.xlsx'  # 作品评论
    view_fileName = 'file/book_view.xlsx'  # 作品视角
    sub_rank_fileName = 'file/book_sub_rank.xlsx'  # 分段排行
    # 大屏
    # page_layout(comments_fileName, style_fileName, style_head4_fileName,
    #             writer_rank_fileName, book_fileName, view_fileName, sub_rank_fileName)
    # 分页组件
    tab_layout(comments_fileName, style_fileName, style_head4_fileName,
               writer_rank_fileName, book_fileName, view_fileName, sub_rank_fileName)
